//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.cloud.loadbalancer.core;

import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import reactor.core.publisher.Mono;


public class RoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer {
	private static final Log log = LogFactory.getLog(RoundRobinLoadBalancer.class);
	final AtomicInteger position;
	final String serviceId;
	ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;

	public RoundRobinLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
		this(serviceInstanceListSupplierProvider, serviceId, (new Random()).nextInt(1000));
	}

	public RoundRobinLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId, int seedPosition) {
		this.serviceId = serviceId;
		this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
		this.position = new AtomicInteger(seedPosition);
	}

	public Mono<Response<ServiceInstance>> choose(Request request) {
		ServiceInstanceListSupplier supplier = (ServiceInstanceListSupplier)this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
		return supplier.get(request).next().map((serviceInstances) -> {
			return this.processInstanceResponse(supplier, serviceInstances);
		});
	}

	private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances) {
		Response<ServiceInstance> serviceInstanceResponse = this.getInstanceResponse(serviceInstances);
		if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
			((SelectedInstanceCallback)supplier).selectedServiceInstance((ServiceInstance)serviceInstanceResponse.getServer());
		}

		return serviceInstanceResponse;
	}

	private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
		if (instances.isEmpty()) {
			String[] s=serviceId.split("\\.");
			if(s.length>1){
				return new EmptyResponse();
			}
			if (log.isWarnEnabled()) {
				log.warn("No servers available for service: " + this.serviceId);
			}

			return new EmptyResponse();
		} else {
			int pos = Math.abs(this.position.incrementAndGet());
			ServiceInstance instance = (ServiceInstance)instances.get(pos % instances.size());
			return new DefaultResponse(instance);
		}
	}
}
